package com.bhudy.base;

import com.bhudy.annotation.PrimaryKey;
import com.bhudy.enums.Relation;
import com.bhudy.exception.SysException;
import com.bhudy.exception.SysExceptionCode;
import com.bhudy.utils.XqhUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;

@Service
public class BaseServiceImpl<T, PK extends Serializable> implements BaseService<T, PK> {

    private Class<T> clazz;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    /*@Autowired
    @Qualifier("twotemplate")
    private JdbcTemplate jdbcTemplateTwo;*/

    public String jdbcTemplateType = "onetemplate";

    @Value("${spring.datasource.driverClassName}")
    private String driverClassName = "org.postgresql.Driver";
    private static Sql baseSql = null;
    private Logger logger = LoggerFactory.getLogger(BaseServiceImpl.class);

    public BaseServiceImpl() {
        Object obj = this.getClass().getGenericSuperclass();
        if (obj instanceof ParameterizedType) {
            ParameterizedType ptClass = (ParameterizedType) obj;
            if (ptClass.getActualTypeArguments()[0] instanceof Class) {
                clazz = (Class) ptClass.getActualTypeArguments()[0];
            }
        }
    }


    private JdbcTemplate getJdbcTemplate() {
        if (jdbcTemplateType.equals("onetemplate")) {
            return this.jdbcTemplate;
        } else {
            return this.jdbcTemplate;
            /*return this.jdbcTemplateTwo;*/
        }
    }

    public Sql getBaseSql() {
        if (baseSql == null) {
            if (driverClassName.toLowerCase().indexOf("mysql") != -1) {
                baseSql = new Mysql();
            } else if (driverClassName.toLowerCase().indexOf("postgresql") != -1) {
                baseSql = new PostGreSql();
            } else if (driverClassName.toLowerCase().indexOf("sqlserver") != -1) {
                baseSql = new SqlServer();
            } else if (driverClassName.toLowerCase().indexOf("oracle") != -1) {
                baseSql = new Oracle();
            } else {
                baseSql = new Mysql();
            }
            logger.debug("实例化数据库访问层：" + baseSql.getClass().getName());
        }
        return baseSql;
    }

    @Override
    public <C> int update(C entity, QueryFilter queryFilter) {
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().updateSql(entity, queryFilter, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().update(sql, argList.toArray());
    }

    @Override
    public <C> int update(C entity) {
        if (entity == null) return 0;
        QueryFilter queryFilter = new QueryFilter();

        String keyName = null;
        Object value = null;
        Field[] fields = clazz.getDeclaredFields(); //获得私有的成员属性
        for (Field field : fields) {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
            if (field.isAnnotationPresent(PrimaryKey.class)) {
                keyName = field.getName();
                try {
                    value = field.get(entity);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                break;
            }
        }

        if (keyName != null) return this.update(entity, new QueryFilter().and(keyName, value));

        throw new SysException(SysExceptionCode.CODE_40015);
    }

    @Override
    public <C> int update(List<C> entityList) {
        if (entityList == null || entityList.size() == 0) return 0;
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().updatesSql(entityList, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().update(sql, argList.toArray());
    }

    @Override
    public int update(String sql, Object... args) {
        return this.getJdbcTemplate().update(sql, args);
    }

    @Override
    public <C> int insert(C entity) {
        if (entity == null) return 0;
        List<C> entityList = new ArrayList<>();
        entityList.add(entity);
        return this.insert(entityList);
    }

    @Override
    public <C> int insert(List<C> entityList) {
        if (entityList == null || entityList.size() == 0) return 0;
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().insertSql(entityList, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().update(sql, argList.toArray());
    }

    @Override
    public int delete(PK... pks) {
        return this.delete(clazz, pks);
    }

    @Override
    public <C> int delete(Class<C> clazz, PK... pks) {
        if (pks == null || pks.length == 0) throw new SysException(SysExceptionCode.CODE_40028);

        String keyName = this.getBaseSql().getKeyName(clazz);
        if (keyName != null) {
            QueryFilter queryFilter = new QueryFilter();
            if (pks.length == 1) {
                queryFilter.and(keyName, pks);
            } else {
                queryFilter.and(keyName, Relation.IN, pks);
            }
            return this.delete(clazz, queryFilter);
        }

        throw new SysException(SysExceptionCode.CODE_40015);
    }

    @Override
    public int delete(QueryFilter queryFilter) {
        return this.delete(clazz, queryFilter);
    }

    @Override
    public <C> int delete(Class<C> clazz, QueryFilter queryFilter) {
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().deleteSql(clazz) + this.getBaseSql().where(queryFilter, argList) + this.getBaseSql().getOrderBy(queryFilter);
        sql = this.getBaseSql().getLimitSql(sql, queryFilter, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().update(sql, argList.toArray());
    }

    @Override
    public List<Map<String, Object>> findMapList(String sql, Object... args) {
        StringBuffer sqlSb = new StringBuffer(sql);
        List<Object> argList = new ArrayList<>();
        if (args != null && args.length != 0) {
            if (args != null && args.length != 0) {
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg instanceof Collection) {
                        Object[] objs = ((Collection<?>) arg).toArray();
                        String str = "(";
                        for (Object obj : objs) {
                            argList.add(obj);
                            str += "?,";
                        }
                        str = str.substring(0, str.length() - 1) + ")";
                        int index = XqhUtils.indexOf(sql, "?", i);
                        sql = sqlSb.replace(index, index + 1, str).toString();
                    } else {
                        argList.add(arg);
                    }
                }
            }
        }

        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().queryForList(sql, argList.toArray());
    }

    @Override
    public int findCount(String sql, Object... args) {
        StringBuffer sqlSb = new StringBuffer(sql);
        List<Object> argList = new ArrayList<>();
        if (args != null && args.length != 0) {
            if (args != null && args.length != 0) {
                for (int i = 0; i < args.length; i++) {
                    Object arg = args[i];
                    if (arg instanceof Collection) {
                        Object[] objs = ((Collection<?>) arg).toArray();
                        String str = "(";
                        for (Object obj : objs) {
                            argList.add(obj);
                            str += "?,";
                        }
                        str = str.substring(0, str.length() - 1) + ")";
                        int index = XqhUtils.indexOf(sql, "?", i);
                        sql = sqlSb.replace(index, index + 1, str).toString();
                    } else {
                        argList.add(arg);
                    }
                }
            }
        }

        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        Integer count = this.getJdbcTemplate().queryForObject(sql, Integer.class, argList.toArray());
        return count == null ? 0 : count;
    }

    @Override
    public T get(PK pk) {
        return this.get(clazz, pk);
    }

    @Override
    public <C> C get(Class<C> clazz, PK pk) {
        if (pk == null) throw new SysException(SysExceptionCode.CODE_40028);

        String keyName = this.getBaseSql().getKeyName(clazz);
        if (keyName != null) return this.find(clazz, new QueryFilter().and(keyName, pk));

        throw new SysException(SysExceptionCode.CODE_40015);
    }

    @Override
    public T find(QueryFilter queryFilter) {
        return this.find(clazz, queryFilter);
    }

    @Override
    public <C> C find(Class<C> clazz, QueryFilter queryFilter) {
        List<C> list = this.findList(clazz, queryFilter);

        if (list == null || list.size() == 0) {
            return null;
        } else if (list.size() > 1) {
            throw new SysException(SysExceptionCode.CODE_40016);
        }
        return list.get(0);
    }

    @Override
    public List<T> findAll() {
        return this.findAll(clazz);
    }

    @Override
    public <C> List<C> findAll(Class<C> clazz) {
        return this.findList(clazz, new QueryFilter());
    }

    @Override
    public List<T> findList(QueryFilter queryFilter) {
        return this.findList(clazz, queryFilter);
    }

    @Override
    public <C> List<C> findList(Class<C> clazz, QueryFilter queryFilter) {
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().selectSql(clazz) + this.getBaseSql().where(queryFilter, argList) + this.getBaseSql().getOrderBy(queryFilter);
        sql = this.getBaseSql().getLimitSql(sql, queryFilter, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().query(sql, new BeanPropertyRowMapper<C>(clazz), argList.toArray());
    }

    @Override
    public int count(QueryFilter queryFilter) {
        return this.count(clazz, queryFilter);
    }

    @Override
    public <C> int count(Class<C> clazz, QueryFilter queryFilter) {
        List<Object> argList = new ArrayList<>();
        String sql = this.getBaseSql().countSql(clazz) + this.getBaseSql().where(queryFilter, argList);
        logger.debug("sql：" + sql);
        logger.debug("params：" + argList.toString());
        return this.getJdbcTemplate().queryForObject(sql, Integer.class, argList.toArray());
    }


    @Override
    public <K, V> Map<K, V> getMapping(String k, String v) {
        return this.getMapping(clazz, k, v);
    }

    @Override
    public <K, V> Map<K, V> getMapping(String k, String v, QueryFilter queryFilter) {
        return this.getMapping(clazz, k, v, queryFilter);
    }

    @Override
    public <C, K, V> Map<K, V> getMapping(Class<C> clazz, String k, String v) {
        return this.getMapping(clazz, k, v, new QueryFilter());
    }

    @Override
    public <C, K, V> Map<K, V> getMapping(Class<C> clazz, String k, String v, QueryFilter queryFilter) {
        List<C> list = this.findList(clazz, queryFilter);
        Map<K, V> map = new HashMap<>();
        if (list != null && list.size() != 0) {
            for (C entity : list) {
                Field[] fields = entity.getClass().getDeclaredFields();
                K key = null;
                V value = null;
                for (Field field : fields) {
                    field.setAccessible(true);
                    if (Modifier.isStatic(field.getModifiers())) continue;// 判断是否是静态属性
                    try {
                        if (field.getName().equals(k)) {
                            key = (K) field.get(entity);
                        } else if (field.getName().equals(v)) {
                            value = (V) field.get(entity);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                map.put(key, value);
            }
        }
        return map;
    }

    @Override
    public String keyword(String name) {
        return baseSql.keyword(name);
    }


    @Override
    public String keywords(String... names) {
        if (names == null || names.length == 0) throw new SysException(SysExceptionCode.CODE_40003);
        StringBuffer stringBuffer = new StringBuffer();
        for (String name : names) {
            stringBuffer.append(baseSql.keyword(name) + " as " + baseSql.keyword(XqhUtils.toLowerCaseFirstOneDel(name)) + ", ");
        }
        return stringBuffer.deleteCharAt(stringBuffer.length() - 2).toString();
    }

}